package genetic.evolution;

import genetic.GeneticProgram;

import java.util.ArrayList;

public class Evolution1 implements EvolutionaryStrategy {

	private class ProbabilityDensity {
		public GeneticProgram gp;
		public double lowerBound;
		public double upperBound;
		
		public ProbabilityDensity(GeneticProgram gp, double lowerBound, double upperBound) {
			this.gp = gp;
			this.lowerBound = lowerBound;
			this.upperBound = upperBound;
		}
	}
	
	private GeneticProgram giveRandomGeneticProgram(double errorSum, ArrayList<ProbabilityDensity> pdf) {
		//TODO check wheter random gives between 0 and 1 (exclusive) or ...
		double choice = Math.random()*errorSum;
		GeneticProgram parent = null;
		for (ProbabilityDensity pd : pdf) {
			if (pd.lowerBound <= choice && choice < pd.upperBound) {
				if (parent != null)
					System.err.println("fatal: 2x probability gewaehlt");
				//TODO return schon hier wenn ok
				parent = pd.gp;
			}
		}
		return parent;
	}
	
	@Override
	public ArrayList<GeneticProgram> evolve(
			ArrayList<GeneticProgram> currentPopulation) {

		ArrayList<GeneticProgram> newPopulation = new ArrayList<GeneticProgram>();
		
		// "WSK-Dichtefunktion" erzeugen
		// TODO in fct auslagern, code aufhuebschen
		ArrayList<ProbabilityDensity> probabilityDensityFunction = new ArrayList<ProbabilityDensity>();

		double sum = 0;
		for (GeneticProgram gp : currentPopulation) {
			double fitness;
			if (gp.getError() <= 0.0001)
				fitness = 10000;
			else
				fitness = 1/gp.getError();
			
			probabilityDensityFunction.add(new ProbabilityDensity(gp, sum, sum+fitness));
			sum += fitness;
		}
		
		// 4/5 der neuen Population erzeugen durch crossover mit den
		// Individuen der alten Population; Selektion WSK proportional Fitness
		// Fitness ist 1/(Summe der Fehlerquadrate)
		for (int i = 0; i < currentPopulation.size() * 4 / 5; i++) {
			GeneticProgram parent1 = giveRandomGeneticProgram(sum, probabilityDensityFunction);
			GeneticProgram parent2 = giveRandomGeneticProgram(sum, probabilityDensityFunction);
			
			newPopulation.add(GeneticProgram.crossover(parent1, parent2));
		}

		// 1/5-1 der neuen Population als Transfer der alten Population
		// Selektion WSK proportional Fitness
		for (int i = 0; i < currentPopulation.size() / 5; i++) {

			newPopulation.add(giveRandomGeneticProgram(sum, probabilityDensityFunction).clone());
		}
		
		return newPopulation;
	}

}
